# _*_ coding: utf-8 _*_
#!/usr/bin/env python

import sys, os
import time
import pygame
import math, random
import pickle
import Numeric
import mode7
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'NumPtr/build/lib.linux-i686-2.5'))
import NumPtr
import xml.sax

try:
	import psyco
	#psyco.full()
	print 'psyco installed'
except:
	print 'psyco not installed'

# Grafikoptionen
screen_size		= screen_width, screen_height = 620, 300
screen_flags	= 0
# Framerate 0 für Maximum
fps_limit		= 25

# Aus Portabilitaetsgruenden. Haengt das Arbeitsverzeichnis vor den Dateinamen
def addWorkPath(filename):
	return os.path.join(os.path.dirname(sys.argv[0]), '../data', filename)

def rotate_point((x, y), angle):
	matrix = [	math.cos(angle), -math.sin(angle),
				math.sin(angle), math.cos(angle)]
	return (	round(x * matrix[0] + y * matrix[1]),
				round(x * matrix[2] + y * matrix[3]))

class ModuleTest(object):
	def __init__(self):
		pygame.init()
		pygame.display.set_caption('Module Test für Mode7')
		pygame.mouse.set_visible(True)
		self.screen	= pygame.display.set_mode(screen_size, screen_flags)
		self.clock	= pygame.time.Clock()
		
		self.image			= pygame.image.load(addWorkPath('Map.png'))
		self.image			= self.image.convert()
		array				= pixel_array	= pygame.surfarray.pixels2d(self.image)
		self.image_size		= (len(array), len(array[0]))
		self.graphic		= Graphic(self)
		self.controll_keys	= {	'key_up':		False,
								'key_down':		False,
								'key_left':		False,
								'key_right':	False}
	
	def runTest(self):
		while(True):
			# Tasten abfragen
			for event in pygame.event.get():
				if event.type == pygame.QUIT:
					pygame.quit()
					return
				if event.type == pygame.KEYDOWN:
					if event.key == pygame.K_ESCAPE:
						return
					elif event.key == pygame.K_LEFT:
						self.controll_keys['key_left'] = True
					elif event.key == pygame.K_RIGHT:
						self.controll_keys['key_right'] = True
					if event.key == pygame.K_UP:
						self.controll_keys['key_up'] = True
					elif event.key == pygame.K_DOWN:
						self.controll_keys['key_down'] = True
				elif event.type == pygame.KEYUP:
					if event.key == pygame.K_LEFT:
						self.controll_keys['key_left'] = False
					elif event.key == pygame.K_RIGHT:
						self.controll_keys['key_right'] = False
					if event.key == pygame.K_UP:
						self.controll_keys['key_up'] = False
					elif event.key == pygame.K_DOWN:
						self.controll_keys['key_down'] = False
			self.graphic.run()
			pygame.display.flip()
			self.clock.tick(fps_limit)

class Graphic(object):
	def __init__(self, parent):
		self.parent		= parent
		self.camera		= 0, 0
		self.scale_x	= 0
		self.scale_y	= 0
		self.space_z	= 20	# Kamerahoehe ueber der Flaeche
		self.horizon	= 0		# Abstand der Zeile-0 vom Horizont
		
		self.output_surface	= pygame.Surface((512, 256))
		self.output_surface.fill((100, 30, 255))
		array				= pygame.surfarray.pixels2d(self.output_surface)
		self.output_size	= (len(array), len(array[0]))
		
		#self.tile			= pygame.image.load(addWorkPath('Testtile.png'))
		# Wichtig, damit es sich um normale 32Bit-Integer handelt. 32Bit Tiefe ist zwar ueblich, aber
		#  nicht garantiert. Auf Systemen mit anderen Vorgaben fuehrt die Umwandlung zu Zeitverlusten
		#self.tile			= self.tile.convert(32)
		#array				= pygame.surfarray.pixels2d(self.tile)
		self.tile_size		= 8, 8 #(len(array), len(array[0]))
		
		self.map			= Map(addWorkPath('maps/Mario_Circuit_1.xml'))
		self.sprites		= pygame.image.load(addWorkPath('Mario_Sprites.png')).convert(32)
		
		self.angle			= 0#math.pi
		self.cam_x			= 100#32 * 8
		self.cam_y			= 200#32 * 8
		self.speed			= 5
		self.scale			= 1.0
		#self.direction		= 0.5
		self.counter		= 0
		self.anim_frame		= 0
	
	def run(self):
		rot_speed	= 0.04
		if self.parent.controll_keys['key_left']:
			self.angle -= rot_speed
			if self.angle < 0:
				self.angle = math.pi * 2 -rot_speed
		elif self.parent.controll_keys['key_right']:
			self.angle += rot_speed
			if self.angle > math.pi * 2:
				self.angle = rot_speed
		if self.parent.controll_keys['key_up']:
			self.cam_x += math.cos(self.angle) * self.speed
			self.cam_y += math.sin(self.angle) * self.speed
		elif self.parent.controll_keys['key_down']:
			self.cam_x -= math.cos(self.angle) * self.speed
			self.cam_y -= math.sin(self.angle) * self.speed
		
		if self.cam_x < 0:
			self.cam_x = 0
		elif self.cam_x >= self.map.map_pixel_size[0]:
			self.cam_x = self.map.map_pixel_size[0] - 1
		if self.cam_y < 0:
			self.cam_y = 0
		elif self.cam_y >= self.map.map_pixel_size[1]:
			self.cam_y = self.map.map_pixel_size[1] - 1
		
		# Kollisionsabfrage
		#if self.map.tilemap[int(self.cam_x / self.map.tile_size[0]), int(self.cam_y / self.map.tile_size[1])] & self.map.tileset_size[0]
		# > self.map.tileset_size[0] / 2:
		#	print 'Kollision!'
		
		outputarray		= pygame.surfarray.pixels2d(self.output_surface)
		screenpointer	= NumPtr.getpointer(outputarray)
		#tilearray	= pygame.surfarray.pixels2d(self.tile)
		#tilepointer	= NumPtr.getpointer(tilearray)
		#mode7.test(screenpointer, tilepointer, 4, 4, 8, 8, 0.1, 0.0, 0.0, 200, 0, 1.0, 1.0)
		#mode7.foo	= 7
		mode7.draw_mode7(	screenpointer,				# Pointer auf die Zielflaeche
							self.output_size[0],		# X-Groesse der Zielflaeche
							self.output_size[1],		# Y-Groesse der Zielflaeche
							
							self.map.tilemap_pointer,	# Pointer auf die Tilemap
							self.map.tilemap_size[0],	# X-Groesse der Tilemap (in Tiles)
							self.map.tilemap_size[1],	# Y-Groesse der Tilemap (in Tiles)
							
							#self.map.tileset_pointer,	# Pointer auf das Tileset
							self.map.raw_tileset_pointer,
							self.map.tileset_size[0],	# X-Groesse des Tilesets (in Tiles)
							self.map.tileset_size[1],	# Y-Groesse des Tilesets (in Tiles)
							self.map.tile_size[0],		# X-Groesse der Tiles
							self.map.tile_size[1],		# Y-Groesse der Tiles
							0x3D,						# Randtile
							
							self.angle,					# Kamerawinkel
							self.cam_x,					# X-Position der Kamera
							self.cam_y,					# Y-Position der Kamera
							1000,						# Kamerahoehe ueber der Flaeche
							0,							# Pixel die der Horizont unter der Nullten Zeile ist
							0.003,						# X-Skalierung
							0.2,						# Y-Skalierung
							self.anim_frame,			# Frame fuer animiert Tiles
							10,							# Nebeldistanz (0 fuer deaktiviert)
							0x805000)					# Nebelfarbe RGB
		del outputarray
		
		font = pygame.font.Font(addWorkPath('Visitor_TT2_BRK.ttf'), 12)
		#self.parent.screen.blit(font.render('sprite %i / xoff: %i / yoff: %i' % (sprite_nr, sprite_xoffset, sprite_yoffset),
		#	False, (255, 10, 10), (0, 0, 0)), (265, 180))
		self.output_surface.blit(self.map.minimap, (0, 0))
		#rect	= (max(self.race.players[0].x_pos / 2 - 2, 0), max(self.race.players[0].y_pos / 2 - 2, 0), 4, 4)
		polygon		= ((0, -10), (10, 10), (0, 5), (-10, 10))
		polygon2	= []
		angle		= self.angle + math.pi * 0.5
		for point in polygon:
			new_point = rotate_point(point, angle)
			#polygon2.append((new_point[0] + self.cam_x / self.map.tile_size[0], new_point[1] + self.cam_y / self.map.tile_size[1]))
			polygon2.append((new_point[0] + self.cam_x / self.map.tile_size[0],
				new_point[1] + self.cam_y / self.map.tile_size[1]))
		pygame.draw.polygon(self.output_surface, (255, 255, 0), polygon2, 2)
		text		= str(1)
		text_size	= font.size(text)
		self.output_surface.blit(font.render(text, False, (255, 255, 0), (0, 0, 0)), (
			(self.cam_x - text_size[1]) / self.map.tile_size[0], (self.cam_y - text_size[1]) / self.map.tile_size[1]))
		
		self.counter += 1
		if not self.counter % 5:
			self.anim_frame += 1
			if self.anim_frame > 21:
				self.anim_frame = 0
				#sys.exit()
		
		#del tilearray
		self.parent.screen.fill((0, 0, 0))
		self.parent.screen.blit(self.output_surface, (5, 5))
		
		sprite_nr = 0
		for name in ['Mario', 'Luigi']:
			sprite_frame		= self.anim_frame #int((math.pi * 2 - self.angle) / (math.pi / 10))
			if sprite_frame > 11:
				sprite_frame	= 22 - sprite_frame
				sprite_yoffset	= sprite_frame / 6 + sprite_nr * 3
				sprite_xoffset	= sprite_frame % 6
				sprite = pygame.transform.flip(self.sprites.subsurface((sprite_xoffset * 32, sprite_yoffset * 32, 32, 32)), True, False)
			else:
				sprite_yoffset	= sprite_frame / 6 + sprite_nr * 3
				sprite_xoffset	= sprite_frame % 6
				sprite = self.sprites.subsurface((sprite_xoffset * 32, sprite_yoffset * 32, 32, 32)).copy()
			self.parent.screen.blit(sprite, (528, 158 + sprite_nr * 70))
			pygame.draw.rect(self.parent.screen, (255, 10, 10), (520, 150 + sprite_nr * 70, 50, 50), 1)
			self.parent.screen.blit(font.render(name, False, (255, 10, 10), (0, 0, 0)), (525, 146 + sprite_nr * 70))
			sprite_nr += 1
		
		text_xpos = 520
		self.parent.screen.blit(font.render('FPS: %i' % self.parent.clock.get_fps(), False, (255, 10, 10), (0, 0, 0)), (text_xpos, 15))
		self.parent.screen.blit(font.render('Ang: rad %.2f' % self.angle, False, (255, 10, 10), (0, 0, 0)), (text_xpos, 35))
		self.parent.screen.blit(font.render('deg %.1f' % math.degrees(self.angle), False, (255, 10, 10), (0, 0, 0)), (text_xpos + 26, 45))
		self.parent.screen.blit(font.render('Pos: %3i %3i' % (self.cam_x, self.cam_y), False, (255, 10, 10), (0, 0, 0)), (text_xpos, 55))
		self.parent.screen.blit(font.render('Scale: %.2f' % self.scale, False, (255, 10, 10), (0, 0, 0)), (text_xpos, 75))
		tile_pos	= int(self.cam_x / self.map.tile_size[0]), int(self.cam_y / self.map.tile_size[1])
		tile		= self.map.tilemap[tile_pos[1]][tile_pos[0]]
		tilecluster	= (self.map.tileset_size[0] / 2) & tile
		self.parent.screen.blit(font.render('TilePos.: %.2i/%.2i' % (tile_pos[0], tile_pos[1]), False, (255, 10, 10), (0, 0, 0)), (text_xpos, 110))
		self.parent.screen.blit(font.render('TileNr.: %.2i' % tile, False, (255, 10, 10), (0, 0, 0)), (text_xpos, 120))
		self.parent.screen.blit(font.render('Tilecluster.: %.2i' % tilecluster, False, (255, 10, 10), (0, 0, 0)), (text_xpos, 130))
		#self.parent.screen.blit(font.render('Kollision: %i' % ((self.map.tilemap[int(self.cam_x / self.map.tile_size[0]), int(self.cam_y / self.map.tile_size[1])] / self.map.tileset_size[1]) / self.map.tileset_size[0] / 2), False, (255, 10, 10), (0, 0, 0)), (text_xpos, 125))


class Map(object):
	def __init__(self, mappath):
		self.loadMap(mappath)
		#self.tileset	= None
		#self.ground_map	= None
		#self.tile_map	= None
		#self.map_size	= (128, 128)
		#self.tile_size	= (8, 8)
	
	def loadRawFiles(self, path):
		directory, filename			= os.path.split(path)
		raw_path					= os.path.join(directory, filename + '.raw')
		if os.path.exists(raw_path):
			print 'loading raw-files'
			self.raw_tileset		= pygame.image.load(open(raw_path, 'r'), 'raw_image.tga').convert(32)
		else:
			print 'create raw-files'
			raw_surf	= self.tileset.copy()
			raw_surf.fill((0, 0, 0))
			tileset_array	= pygame.surfarray.pixels2d(self.tileset)
			raw_array	= pygame.surfarray.pixels2d(raw_surf)
			offset_1d	= 0
			for tile_y in range(self.tileset.get_height() / self.tile_size[0]):
				for tile_x in range(self.tileset.get_width() / self.tile_size[1]):
					for pixel_y in range(self.tile_size[0]):
						for pixel_x in range(self.tile_size[1]):
							pixel = tileset_array[tile_x * self.tile_size[0] + pixel_x][tile_y * self.tile_size[1] + pixel_y]
							raw_array[offset_1d % self.tileset.get_width()][offset_1d / self.tileset.get_height()] = pixel
							offset_1d += 1
			del raw_array
			pygame.image.save(raw_surf, raw_path)
			self.raw_tileset		= raw_surf
		self.raw_tileset_pointer	= NumPtr.getpointer(pygame.surfarray.pixels2d(self.raw_tileset))
	
	def loadMap(self, path):
		tileset_path			= addWorkPath('maps/Tileset_Mario_Circuit')
		self.tileset			= pygame.image.load(tileset_path + '.png').convert(32)
		#self.tileset			= pygame.image.load(addWorkPath('maps/Tilemap_big.png')).convert(32)
		self.tileset_pointer	= NumPtr.getpointer(pygame.surfarray.pixels2d(self.tileset))
		self.tile_size			= (8, 8)
		self.tileset_size		= (self.tileset.get_width() / self.tile_size[0], self.tileset.get_height() / self.tile_size[1])
		self.loadRawFiles(tileset_path)
		print 'Lade Map...',
		self.parseXMLMap(addWorkPath('maps/Mario_Circuit_1.map.xml'))
		print 'Fertig'
		#self.tilemap	= Numeric.array([[0x01, 0x02], [0x02, 0x01]], Numeric.Int32)
		#self.tilemap			= Numeric.array([[0x10, 0x08, 0x08, 0x08, 0x08], [0x08, 0x01, 0x02, 0x03, 0x08], [0x08, 0x03, 0x01, 0x02, 0x08], [0x08, 0x02, 0x03, 0x01, 0x08], [0x08, 0x08, 0x08, 0x08, 0x08]], Numeric.Int32)
		#self.tilemap			= Numeric.array(pickle.load(file(addWorkPath('maps/map_generator/Donut_Plains_1.map'), 'r')), Numeric.Int32)
		#self.tilemap = []
		#for y in range(64):
			#self.tilemap.append([])
			#for x in range(64):
				#if y < 32 and x < 16:
					#self.tilemap[y].append(1023)
				#else:
					#self.tilemap[y].append(1)
		#self.tilemap = Numeric.array(self.tilemap, Numeric.Int32)

		#mapfile = file(addWorkPath(addWorkPath('maps/Tilemap_big.map')), 'w')
		#pickle.dump(self.tilemap, mapfile)
		#mapfile.close()
		#self.tilemap			= Numeric.array(pickle.load(file(addWorkPath('maps/Tilemap_big.map'), 'r')), Numeric.Int32)
		
		
		#self.tilemap			= Numeric.array(pickle.load(file(addWorkPath('maps/map_generator/Mario_Circuit_1.map'), 'r')), Numeric.Int32)
		
		self.tilemap_pointer	= NumPtr.getpointer(self.tilemap)
		self.tilemap_size		= (len(self.tilemap[0]), len(self.tilemap))
		#print self.tileset_size
		self.map_pixel_size		= (self.tilemap_size[0] * self.tile_size[0], self.tilemap_size[1] * self.tile_size[1])
		
		# Minimap erzeugen
		self.minimap			= pygame.Surface((self.tilemap_size[0], self.tilemap_size[1]))
		minimap_array			= pygame.surfarray.pixels2d(self.minimap)
		for y in range(self.tilemap_size[1]):
			for x in range(self.tilemap_size[0]):
				tile			= self.tilemap[y][x]
				if tile % self.tileset_size[0] < self.tileset_size[0] / 2 and tile < (self.tileset_size[1] / 2) * self.tileset_size[0]:
					minimap_array[x][y] = 0xD0D0FF
				else:
					minimap_array[x][y] = 0xFF00FF
				#if tile < (self.tileset_size[1] / 2) * self.tileset_size[0]:
					#if tile % self.tileset_size[0] < self.tileset_size[0] / 2:
						## Sektor 1
						#minimap_array[x][y] = 0xFFFFFF
					#else:
						## Sektor 2
						#minimap_array[x][y] = 0xFF00FF
				#else:
					#if tile % self.tileset_size[0] < self.tileset_size[0] / 2:
						## Sektor 3
						#minimap_array[x][y] = 0xFF0000
					#else:
						## Sektor 4
						#minimap_array[x][y] = 0x000000

		self.minimap.set_colorkey((255, 0, 255))
		self.minimap.set_alpha(200)
	
	def parseXMLMap(self, path):
		tilemap			= []
		xml.sax.parse(path, XMLHander(tilemap))
		self.tilemap	= Numeric.array(tilemap, Numeric.Int32)


class XMLHander(xml.sax.ContentHandler):
	def __init__(self, tilemap):
		self.tilemap		= tilemap
		self.active_tag		= None
		self.row=0
	
	def startDocument(self):
		self.active_tag		= None

	def endDocument(self):
		return self.tilemap

	def startElement(self, name, attrs):
		self.row+=1
		self.active_tag		= None
		if name == 'tilerow':
			self.tilemap.append([])
			self.active_tag	= 'tilerow'
		if name == 'tile':
			self.active_tag	= 'tile'
			
	def endElement(self,name):
		#self.row+=1
		self.active_tag	= None
		#if name == "tilerow":
			#self.tilemap.append(self.tilerow)

	def characters(self,data):
		if self.active_tag == 'tile':
			self.tilemap[-1].append(int(data))

if __name__ == '__main__':
	main = ModuleTest()
	main.runTest()
